﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;

namespace CommentHero
{
    internal sealed class ConfigReader
    {
        #region Singletonパターン
        private static volatile ConfigReader m_instance;
        private static object m_syncRoot = new object();

        /// <summary>
        /// インスタンス取得
        /// </summary>
        internal static ConfigReader Instance
        {
            get
            {
                if (m_instance == null)
                {
                    lock (m_syncRoot)
                    {
                        if (m_instance == null)
                        {
                            m_instance = new ConfigReader();
                        }
                    }
                }

                m_instance.Initialize();
                return m_instance;
            }
        }
        #endregion

        #region 設定ファイル操作
        /// <summary>
        /// 設定ファイル再読み込み
        /// </summary>
        internal static void LoadConfig()
        {
            m_instance._init = false;

            m_instance.Initialize();
        }

        /// <summary>
        /// 設定ファイル更新
        /// </summary>
        /// <param name="nodePath">ノードパス</param>
        /// <param name="attributeName">属性名(属性がない場合はnull指定)</param>
        /// <param name="newValue">更新値</param>
        internal static void UpdateNode(string nodePath, string attributeName, string newValue)
        {
            try
            {
                XmlNode targetNode = m_instance._doc.SelectSingleNode(nodePath);

                if (string.IsNullOrEmpty(attributeName))
                {
                    targetNode.InnerText = newValue;
                }
                else
                {
                    targetNode.Attributes[attributeName].Value = newValue;
                }
            }
            catch
            {

            }
        }

        /// <summary>
        /// 設定ファイル更新(複数行)
        /// </summary>
        /// <param name="nodePath">ノードパス</param>
        /// <param name="newXml">更新値XML</param>
        internal static void UpdateNodeXml(string nodePath, string newXml)
        {
            try
            {
                XmlNode targetNode = m_instance._doc.SelectSingleNode(nodePath);

                targetNode.InnerXml = newXml;
            }
            catch
            {

            }
        }

        /// <summary>
        /// 設定ファイル保存
        /// </summary>
        internal static void SaveXml()
        {
            try
            {
                if (File.Exists(m_instance._configPath))
                {
                    m_instance._doc.Save(m_instance._configPath);
                }
                else
                {
                    //例外
                }
            }
            catch
            {

            }
        }
        #endregion
        
        #region コンストラクタ
        private ConfigReader()
        {
        }
        #endregion

        #region フィールド
        private bool _init = false; // 初期化済みフラグ
        private XmlDocument _doc; // XMLドキュメント
        private string _configPath; // 設定ファイルのパス

        private CustomCommentAttributes[] _customCommentAttributes; // カスタムコメント属性
        private string _dateFormat; // 日付書式
        private string _author; // 作成者
        private Comments _csComments; // C#用コメント定義オブジェクト
        private Comments _vbComments; // VB用コメント定義オブジェクト
        #endregion

        #region プロパティ
        /// <summary>
        /// カスタムコメント属性オブジェクト
        /// </summary>
        public CustomCommentAttributes[] CustomCommentAttributes
        {
            get
            {
                return this._customCommentAttributes;
            }
        }

        /// <summary>
        /// 日付書式
        /// </summary>
        public string DateFormat
        {
            get
            {
                return this._dateFormat;
            }
        }

        /// <summary>
        /// 作成者
        /// </summary>
        public string Author
        {
            get
            {
                return this._author;
            }
        }

        /// <summary>
        /// C#用コメント定義オブジェクト
        /// </summary>
        public Comments CSComments
        {
            get
            {
                return this._csComments;
            }
        }

        /// <summary>
        /// VB用コメント定義オブジェクト
        /// </summary>
        public Comments VBComments
        {
            get
            {
                return this._vbComments;
            }
        }
        #endregion

        #region 設定ファイル読み込み
        /// <summary>
        /// 設定ファイルの読み込み
        /// </summary>
        private void Initialize()
        {
            if (this._init)
                return;

            try
            {
                //TODO:設定ファイル名取得をもっとスマートに
                //Logger.Output("設定ファイル読み込み開始");

                // 実行ファイルパスを取得
                Assembly asm = Assembly.GetExecutingAssembly();
                string exeRoot = asm.Location;
                //string configPath = string.Empty;

                //Logger.Output("exeRoot=" + exeRoot);
                // 設定ファイルパス取得
                try
                {

                    Configuration config = ConfigurationManager.OpenExeConfiguration(exeRoot);
                    this._configPath = config.FilePath;

                    //Logger.Output("configPath=" + configPath);

                    asm = null;

                    // 設定ファイル読み込み
                    if (File.Exists(this._configPath))
                    {
                        this._doc = new XmlDocument();
                        this._doc.Load(this._configPath);
                    }
                }
                catch (Exception)
                {
                    //Logger.Output("Config読み込みで例外発生");

                    this._configPath = @"C:\Documents and Settings\tmukaiyama\My Documents\Visual Studio 2005\Addins\CommentHero.dll.config";
                    //throw new Exception(string.Format("exeRoot=[{0}],configPath=[{1}]", exeRoot, configPath), ex);
                }

                if (this._doc != null)
                {

                    // <customize>要素の取得
                    XmlElement customizeElement = FindConfigElement("//customize");
                    if (customizeElement != null)
                    {
                        ProcessCustomizeElement(customizeElement);
                    }
                    customizeElement = null;

                    // <cs>要素の取得
                    XmlElement csCommentsElement = FindConfigElement("//cs");
                    if (csCommentsElement != null)
                    {
                        this._csComments = new Comments(csCommentsElement);

                    }
                    csCommentsElement = null;

                    // <vb>要素の取得
                    XmlElement vbCommentsElement = FindConfigElement("//vb");
                    if (vbCommentsElement != null)
                    {
                        this._vbComments = new Comments(vbCommentsElement);

                    }
                    vbCommentsElement = null;

                }

                // 初期処理済み
                this._init = true;
            }
            catch
            {
            }
            finally
            {
            }
        }

        /// <summary>
        /// 設定ファイルの要素取得
        /// </summary>
        /// <param name="path">ノード</param>
        /// <returns>XmlElementオブジェクト</returns>
        private XmlElement FindConfigElement(string path)
        {
            if (this._doc == null)
                return null;

            if (string.IsNullOrEmpty(path))
                return null;

            try
            {
                XmlElement root = this._doc.DocumentElement;
                XmlElement element = (XmlElement)root.SelectSingleNode(path);
                return element;
            }
            catch
            {
            }

            return null;
        }

        /// <summary>
        /// &lt;customize&gt;要素の展開処理
        /// </summary>
        /// <param name="customizeElement">&lt;customize&gt;要素</param>
        private void ProcessCustomizeElement(XmlElement customizeElement)
        {
            // カスタムコメント1～5の属性取得
            this._customCommentAttributes = new CustomCommentAttributes[5];

            for (int i = 0; i < this._customCommentAttributes.Length; i++)
            {
                XmlNode customCommentAttribute = customizeElement.SelectSingleNode("custom" + (i + 1).ToString());
                this._customCommentAttributes[i] = new CustomCommentAttributes(customCommentAttribute);
            }

            // 日付書式の属性取得
            XmlNode dateFormatAttribute = customizeElement.SelectSingleNode("dateFormat");
            this._dateFormat = dateFormatAttribute.Attributes["value"].Value;

            // 作成者の属性取得
            XmlNode authorAttribute = customizeElement.SelectSingleNode("author");
            this._author = authorAttribute.Attributes["value"].Value;
        }
        #endregion
    }

}
